﻿ using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;
    using System.IO.Pipes;
    using System.Security.Principal;
    using System.Threading;
using System.ComponentModel;
namespace PipeHitter
{
    public delegate string OnPipeMessage(string message);
    public delegate string OnPipeException(Exception ex);
    public class PipeServer : IDisposable
    {
        private string pipename;
        private NamedPipeServerStream pipe;
        private OnPipeMessage onpipe;
        private OnPipeException onexception;
        private BackgroundWorker thread;

        public PipeServer(string pipename, OnPipeMessage onMessageDelegate = null, OnPipeException onExceptionDelegate =null)
        {
            this.pipename = pipename;
            if (onMessageDelegate == null)
            {
                onpipe = new OnPipeMessage((string message) => { return "ECHO " + message; });
            }
            else
            {
                onpipe = onMessageDelegate;
            }
            this.onexception = onExceptionDelegate;
        }

        public void Connect()
        {
            pipe = new NamedPipeServerStream(pipename, PipeDirection.InOut, 2, PipeTransmissionMode.Byte);
            thread = new BackgroundWorker();
            thread.WorkerSupportsCancellation = true;
            thread.WorkerReportsProgress = false;
            thread.DoWork += new DoWorkEventHandler(thread_DoWork);
            thread.RunWorkerAsync();
        }

        private bool ContinueRunning()
        {
            return (this.thread != null && !this.thread.CancellationPending && this.pipe != null);
        }

        void thread_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                while (this.ContinueRunning())
                {
                    try
                    {
                        pipe.WaitForConnection();
                        var util = new PipeStreamUtil();
                        var req = util.ReadString(pipe);
                        var resp = this.onpipe(req);
                        util.WriteString(pipe, resp);
                        pipe.WaitForPipeDrain();
                        pipe.Disconnect();
                    }
                    catch (Exception ex)
                    {
                        this.DoOnException(ex);
                        if (this.ContinueRunning())
                        {
                            try { pipe.Close(); }
                            catch { }
                            try { pipe.Dispose(); }
                            catch { }
                            pipe = new NamedPipeServerStream(pipename, PipeDirection.InOut, 2, PipeTransmissionMode.Byte);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this.DoOnException(ex);
            }
             this.pipe = null;
                    
        }

        private void DoOnException(Exception ex)
        {
            if (this.onexception != null)
            {
                try
                {
                    this.onexception(ex);
                }
                catch { }
            }
        }


        public void Close()
        {
            this.Dispose();
        }

        public void Dispose()
        {
            if (this.thread != null)
            {
                this.thread.CancelAsync();
            }
            if (pipe != null)
            {
                try
                {
                    try { if (pipe.IsConnected) { pipe.Disconnect(); }     } catch { }
                    try { pipe.Close(); }  catch { }
                    try { pipe.Dispose(); }  catch { }
                    try { pipe.EndWaitForConnection(null); }   catch { }
                    try
                    {
                        this.onexception = null;
                        while (this.pipe != null) //real shit work around to get the pipe to stop blocking on the waitforconnection method
                        {
                            try
                            {
                                using (var fake = new NamedPipeClientStream(this.pipename))
                                {
                                    fake.Connect(500);
                                    fake.Close();
                                }
                            }
                            catch { }
                        }
                    }
                    catch { }
      
                }
                catch { }
            }

        }
    }
}
